package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName TPMonitorFilter
 * @Description
 * @Author playboy
 * @Date 2021/6/19 2:10 下午
 * @Version 1.0
 **/
@Activate(group = CommonConstants.CONSUMER)
public class TPMonitorFilter implements Filter {
	private static Logger log = LoggerFactory.getLogger(TPMonitorFilter.class);

	//每一个接口对应的最近12个5s内的耗时统计即1分钟内的统计
	private static Map<String, TimeCount> oneMinuteMap = new ConcurrentHashMap<>();

	//每一个接口最近5s内的接口耗时统计数据集
	private static Map<String, List<Long>> fiveSecondMap = new ConcurrentHashMap<>();

	static private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);

	static {
		scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				for (Map.Entry<String, List<Long>> entry : fiveSecondMap.entrySet()) {
					try {
						List<Long> fiveSecond = entry.getValue();
						TimeCount timeCount = oneMinuteMap.get(entry.getKey());
						if (timeCount == null) {
							timeCount = new TimeCount();
							oneMinuteMap.put(entry.getKey(), timeCount);
						}
						timeCount.add(fiveSecond);
						fiveSecond.clear();
						List<Long> list = timeCount.getList();
						//计算list的90%和99%
						//TP90
						int index = (int) (list.size() * 0.9F);
						Long spend1 = list.get(index);
						//TP99
						int index9 = (int) (list.size() * 0.99F);
						Long spend2 = list.get(index9);

						log.info("=========================================");
						log.info("当前滑动窗口数据量为：{}", timeCount.getOneMinute().size());
						log.info(entry.getKey());
						log.info("最近一分钟内检测到的请求总数:{}", list.size());
						log.info("TP90的位置：{}", index);
						log.info("{}接口的TP90为{}毫秒", entry.getKey(), spend1);
						log.info("TP99的位置：" + index9);
						log.info("{}接口的TP99为{}毫秒", entry.getKey(), spend2);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}, 5L, 5L, TimeUnit.SECONDS);
	}


	@Override
	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
		long start = 0L;
		try {
			start = System.currentTimeMillis();
			Result invoke = invoker.invoke(invocation);
			return invoke;
		} finally {
			long end = System.currentTimeMillis();
			long spend = end - start;
			String id = invocation.getServiceName() + "." + invocation.getMethodName();
			List<Long> integers = fiveSecondMap.get(id);
			if (integers == null) {
				synchronized (this) {
					integers = fiveSecondMap.get(id);
					if (integers == null) {
						integers = new CopyOnWriteArrayList<>();
						fiveSecondMap.put(id, integers);
					}
				}
			}
			integers.add(spend);
		}
	}

	/**
	 * 用来统计最近一分钟内方法耗时
	 */
	static class TimeCount {
		//创建一个12大小的list，用来存放每5秒内的数据
		private List<List<Long>> oneMinute = new ArrayList(12);

		public List<List<Long>> getOneMinute() {
			return oneMinute;
		}

		/*
				将最近5s内的统计数据放入一分钟之内的统计数据，并将最早的5s统计数据剔除
				 */
		public void add(List<Long> fiveSeconds) {
			if (oneMinute.size() == 12) {
				oneMinute.remove(11);
			}
			oneMinute.add(0, new ArrayList<>(fiveSeconds));
		}

		/**
		 * 获取一分钟之内的所有数据，并且排序
		 *
		 * @return
		 */
		public List<Long> getList() {
			List<Long> integers = new ArrayList<>();
			for (List<Long> integerList : oneMinute) {
				integers.addAll(integerList);
			}
			integers.sort(new Comparator<Long>() {
				@Override
				public int compare(Long o1, Long o2) {
					return o1.compareTo(o2);
				}
			});
			return integers;
		}

	}

}
